package fun.yuxi.gateway.router;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import fun.yuxi.gateway.properties.NacosDynamicRouteProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * Nacos路由数据源实现
 *
 * @author 剧终
 * @version V1.0
 * @date 2021年09月03日 11:22
 */
@Slf4j
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository {

    private final NacosDynamicRouteProperties dynamicRouteProperties;
    private final ApplicationEventPublisher publisher;
    private ConfigService configService;

    public NacosRouteDefinitionRepository(ApplicationEventPublisher publisher, NacosDynamicRouteProperties dynamicRouteProperties) {
        this.publisher = publisher;
        this.dynamicRouteProperties = dynamicRouteProperties;
        try {
            this.configService = NacosFactory.createConfigService(dynamicRouteProperties.getServerAddr());
        } catch (NacosException e) {
            log.error("Nacos 监听初始化失败", e);
        }
        addListener();
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        try {
            String dataId = dynamicRouteProperties.getDataId();
            String groupId = dynamicRouteProperties.getGroupId();
            String content = configService.getConfig(dataId, groupId, 5000);
            if (StrUtil.isNotBlank(content)) {
                log.debug("\n从Nacos获取的路由信息:\n{}", JSONUtil.formatJsonStr(content));
                List<RouteDefinition> routeDefinitions = toList(content, RouteDefinition.class);
                return Flux.fromIterable(routeDefinitions);
            }
        } catch (NacosException e) {
            log.error("从Nacos获取路由定义失败", e);
        }
        return Flux.fromIterable(CollUtil.newArrayList());
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return null;
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return null;
    }

    /**
     * 添加Nacos监听
     */
    private void addListener() {
        try {
            String dataId = dynamicRouteProperties.getDataId();
            String groupId = dynamicRouteProperties.getGroupId();
            configService.addListener(dataId, groupId, new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String configInfo) {
                    publisher.publishEvent(new RefreshRoutesEvent(this));
                }
            });
        } catch (Exception exception) {
            log.error("Nacos 监听异常", exception);
        }
    }

    /**
     * 将json字符串 数组转实体List
     *
     * @param json  json数组
     * @param clazz 泛型定义
     * @return 实体List
     */
    public static <T> List<T> toList(String json, Class<T> clazz) {
        if (StrUtil.isBlank(json)) {
            return Collections.emptyList();
        }
        JSONArray objects = JSONUtil.parseArray(json);
        return JSONUtil.toList(objects, clazz);
    }
}
